package one.stand.zuul.filter;

import brave.Tracing;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.netflix.util.Pair;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import one.stand.util.IOUtil;
import one.stand.zuul.config.ErrorReporter;
import one.stand.zuul.util.ErrorMsgTemplate;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;

import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.Objects;

/**
 * AccessFilter
 * Created by chengjiarui on 2019-06-11.
 */
public class ResponseFilter extends ZuulFilter {
    private static final Logger log = LoggerFactory.getLogger(ResponseFilter.class);
    private final ErrorReporter errorReporter;
    private Tracing tracing;

    public ResponseFilter(ErrorReporter errorReporter, Tracing tracing) {
        this.errorReporter = errorReporter;
        this.tracing = tracing;
    }

    @Override
    public String filterType() {
        return FilterConstants.POST_TYPE;
    }

    @Override
    public int filterOrder() {
        return 1;
    }

    @Override
    public boolean shouldFilter() {
        return true;
    }

    @Override
    public Object run() throws ZuulException {
        Integer status = null;
        String contentType = null;
        String content = null;
        try {
            status = RequestContext.getCurrentContext().getResponseStatusCode();
            String url = RequestContext.getCurrentContext().getRequest().getRequestURI();
            contentType = header(HttpHeaders.CONTENT_TYPE);
            content = response(url, status, contentType);
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
        log.debug("status:{} type:{} content:{}", status, contentType, content);
        log.info("status:{} content:{}", status, content);
        return null;
    }

    private String response(String url, Integer status, String contentType) throws IOException {
        if (!(HttpStatus.OK.value() == status && contentType != null && contentType.contains(MediaType.APPLICATION_JSON_VALUE))) {
            if (HttpStatus.OK.value() != status) {
                error(url, String.valueOf(status));
            }
            return null;
        }

        InputStream stream = RequestContext.getCurrentContext().getResponseDataStream();
        String body = IOUtil.stream2String(stream);

//        if (StringUtils.isBlank(body)) {
//            OutputStream outputStream = RequestContext.getCurrentContext().getResponse().getOutputStream();
//        }

        if (StringUtils.isNoneBlank(body)) {
            RequestContext.getCurrentContext().setResponseBody(body);
        }

        if (StringUtils.isNoneBlank(body) && body.contains("code")) {
            JSONObject jsonObject = JSON.parseObject(body);
            Integer code = jsonObject.getInteger("code");
            if (code != null && (code == 0 || code == 2 || (code >= 200 && code < 500))) {
                return String.valueOf(code);
            } else {
                error(url, body);
            }
        }

        return body;
    }

    private void error(String url, String content) {
        if (Objects.isNull(errorReporter)) {
            log.warn("errorReporter is null");
            return;
        }
        ErrorMsgTemplate errorMsgTemplate = ErrorMsgTemplate.builder()
                .application("zuul")
                .url(url)
                .traceId(tracing.currentTraceContext().get().traceIdString())
                .message(content)
//                                .userId()
//                                .user()
                .build();
        errorReporter.send(errorMsgTemplate);
    }

    private String header(String header) {
        String headerValue = RequestContext.getCurrentContext().getResponse().getHeader(header); // when error
        if (StringUtils.isNoneBlank(headerValue)) return headerValue;

        // forward
        List<Pair<String, String>> headerList = RequestContext.getCurrentContext().getOriginResponseHeaders();
        for (Pair<String, String> pair : headerList) {
            if (header.equalsIgnoreCase(pair.first())) {
                return pair.second();
            }
        }
        return null;
    }
}
